{
  Copyright 2012 Sergey Ostanin

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
}

unit SectionScore;

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, ComCtrls, TestSession,
  MiscUtils, VisualUtils, LCLType;

type
  TSectionScoreFrame = class(TFrame)
    lvSections: TListView;
    procedure lvSectionsColumnClick(Sender: TObject; Column: TListColumn);
    procedure lvSectionsDblClick(Sender: TObject);
    procedure lvSectionsKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    FSessionResult: TSessionResult;
    FSortColumnIndex: Integer;
    FSortBackwards: Boolean;
    FOnActivateSection: TNotifyEvent;
    procedure Display;
    function CompareSections(const Left, Right: Integer): Integer;
    function GetFieldColumnIndex(Field: Integer): Integer;
    procedure ActivateSection;
    function GetSelectedSectionIndex: Integer;
    procedure SetSelectedSectionIndex(Value: Integer);
    { private declarations }
  public
    procedure SetUp(SessionResult: TSessionResult);

    property OnActivateSection: TNotifyEvent read FOnActivateSection write FOnActivateSection;
    property SelectedSectionIndex: Integer read GetSelectedSectionIndex write SetSelectedSectionIndex;
    { public declarations }
  end;

implementation

{$R *.lfm}

const
  FIELD_SECTION_INDEX = 0;
  FIELD_PART_RIGHT = 1;
  FIELD_WEIGHT_RIGHT = 2;
  FIELD_WEIGHT_TOTAL = 3;
  FIELD_QUESTION_COUNT = 4;

{ TSectionScoreFrame }

procedure TSectionScoreFrame.lvSectionsColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  if Column.Index = FSortColumnIndex then
    FSortBackwards := not FSortBackwards
  else
  begin
    FSortColumnIndex := Column.Index;
    FSortBackwards := Column.Index <> 0;
  end;
  Display;
end;

procedure TSectionScoreFrame.lvSectionsDblClick(Sender: TObject);
begin
  ActivateSection;
end;

procedure TSectionScoreFrame.lvSectionsKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if (Key = VK_RETURN) and (Shift = []) then
  begin
    Key := 0;
    ActivateSection;
  end;
end;

procedure TSectionScoreFrame.Display;
var
  i, j, Field, Saved: Integer;
  r: TSectionResult;
  Item: TListItem;
  SectionIndices: TIntegerList;
  s: String;
begin
  Saved := SelectedSectionIndex;

  SectionIndices := TIntegerList.Create;
  try
    for i := 0 to FSessionResult.SectionResultCount-1 do
      SectionIndices.Add(i);
    SectionIndices.SortEx(CompareSections);

    lvSections.BeginUpdate;
    try
      lvSections.Clear;

      for i in SectionIndices do
      begin
        r := FSessionResult.SectionResults[i];
        Item := lvSections.Items.Add;
        {$HINTS OFF}
        Item.Data := Pointer(i);
        {$HINTS ON}
        Item.Caption := r.Name;
        for j := 1 to lvSections.Columns.Count-1 do
        begin
          Field := lvSections.Columns[j].Tag;
          case Field of
            FIELD_PART_RIGHT: s := IntToStr(Round(r.PartRight*100));
            FIELD_WEIGHT_RIGHT: s := ConvertFloatToString(r.WeightRight, 2,
              GetUiDecimalSeparator, TRUE);
            FIELD_WEIGHT_TOTAL: s := IntToStr(r.WeightTotal);
            FIELD_QUESTION_COUNT: s := IntToStr(r.QuestionCount);
            else
              Assert( FALSE );
          end;
          Item.SubItems.Add(s);
        end;
      end;
    finally
      lvSections.EndUpdate;
    end;
  finally
    SectionIndices.Free;
  end;

  SelectedSectionIndex := Saved;
  DisplayListViewSortIndicator(lvSections, FSortColumnIndex, FSortBackwards);
end;

function TSectionScoreFrame.CompareSections(const Left, Right: Integer): Integer;
var
  SortField: Integer;
  l, r: TSectionResult;
begin
  SortField := lvSections.Columns[FSortColumnIndex].Tag;
  if SortField = FIELD_SECTION_INDEX then
    Result := CompareIntegers(Left, Right)
  else
  begin
    l := FSessionResult.SectionResults[Left];
    r := FSessionResult.SectionResults[Right];
    case SortField of
      FIELD_PART_RIGHT: Result := CompareSingles(l.PartRight, r.PartRight);
      FIELD_WEIGHT_RIGHT: Result := CompareDoubles(l.WeightRight, r.WeightRight);
      FIELD_WEIGHT_TOTAL: Result := CompareIntegers(l.WeightTotal, r.WeightTotal);
      FIELD_QUESTION_COUNT: Result := CompareIntegers(l.QuestionCount, r.QuestionCount);
      else
        Assert( FALSE );
    end;
  end;

  if FSortBackwards then
    Result := -Result;

  if Result = 0 then
    Result := CompareIntegers(Left, Right);
end;

function TSectionScoreFrame.GetFieldColumnIndex(Field: Integer): Integer;
begin
  for Result := 0 to lvSections.Columns.Count-1 do
    if lvSections.Columns[Result].Tag = Field then
      Exit;
  Assert( FALSE );
end;

procedure TSectionScoreFrame.ActivateSection;
begin
  if (lvSections.Selected <> nil) and Assigned(FOnActivateSection) then
    FOnActivateSection(Self);
end;

function TSectionScoreFrame.GetSelectedSectionIndex: Integer;
begin
  if lvSections.Selected = nil then
    Result := -1
  else
    {$HINTS OFF}
    Result := Integer(lvSections.Selected.Data);
    {$HINTS ON}
end;

procedure TSectionScoreFrame.SetSelectedSectionIndex(Value: Integer);
var
  Item: TListItem;
begin
  if Value = -1 then
    lvSections.Selected := nil
  else
  begin
    {$HINTS OFF}
    Item := lvSections.FindData(0, Pointer(Value), TRUE, FALSE);
    {$HINTS ON}
    if Item <> nil then
    begin
      lvSections.Selected := Item;
      Item.MakeVisible(FALSE);
    end;
  end;
  lvSections.ItemFocused := lvSections.Selected;
end;

procedure TSectionScoreFrame.SetUp(SessionResult: TSessionResult);
begin
  FSessionResult := SessionResult;

  if not FSessionResult.Policy.SectionPercentCorrect then
    lvSections.Columns.Delete(GetFieldColumnIndex(FIELD_PART_RIGHT));
  if not FSessionResult.Policy.SectionPoints then
  begin
    lvSections.Columns.Delete(GetFieldColumnIndex(FIELD_WEIGHT_RIGHT));
    lvSections.Columns.Delete(GetFieldColumnIndex(FIELD_WEIGHT_TOTAL));
  end;
  if not FSessionResult.Policy.SectionQuestionCount then
    lvSections.Columns.Delete(GetFieldColumnIndex(FIELD_QUESTION_COUNT));

  Display;

  AutoSizeListViewColumns(lvSections);
  MakeListViewDoubleBuffered(lvSections);
end;

end.

